﻿using AuthExtensions;
using AuthExtensions.Auth;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace MultiTenant.FreeSql;

public static class AuthExtensions
{
    public static void AddTenantAuth(this IServiceCollection services, IConfigurationSection section)
    {
        var config = section.Get<AuthConfig>() ?? new AuthConfig();
        services.Configure<AuthConfig>(section);
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddSingleton<IAuthUser, AuthUser>();
        services.AddSingleton<IAuthToken, AuthToken>();
        //  services.AddTransient<CustomJwtBearerHandler>();

        services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)

            .AddJwtBearer(options =>
            {
                var key = Encoding.ASCII.GetBytes(config.SigningKey);
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidIssuer = config.ValidIssuer,
                    ValidateAudience = true,
                    ValidAudience = config.ValidAudience,
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(key),
                    ValidateLifetime = true,//                   
                    ClockSkew = TimeSpan.FromMinutes(config.ExpirationMinutes) //   
                };
                options.Events = new JwtBearerEvents
                {
                    OnMessageReceived = context =>
                    {
                        if (config.QueryToken)
                        {
                            var accessToken = context.Request.Query["access_token"];
                            // If the request is for our hub... {/Tenant1/MessageHub}
                            var path = context.HttpContext.Request.Path;
                            if (path != null && path.Value != null && !string.IsNullOrEmpty(accessToken) &&
                                (config.QueryPaths != null && config.QueryPaths.Length > 0
                                && config.QueryPaths.Any(x => path.Value.ToLower().Contains(x.ToLower()))))
                            {
                                // Read the token out of the query string
                                context.Token = accessToken;
                            }
                        }
                        return Task.CompletedTask;
                    }
                };
            });






        /* .AddScheme<JwtBearerOptions, JwtBearerHandler>(JwtBearerDefaults.AuthenticationScheme,
         options =>
         {
             options.TokenValidationParameters = new TokenValidationParameters
             {
                 ValidateIssuer = true,
                 ValidateAudience = true,
                 ValidateLifetime = true,
                 ValidateIssuerSigningKey = true,
             };
             if (config.QueryToken)
             {

                 options.Events = new JwtBearerEvents
                 {
                     OnMessageReceived = context =>
                     {
                         var accessToken = context.Request.Query["access_token"];
                         // If the request is for our hub... {/Tenant1/MessageHub}
                         var path = context.HttpContext.Request.Path;
                         if (path != null && path.Value != null && !string.IsNullOrEmpty(accessToken) &&
                             (config.QueryPaths != null && config.QueryPaths.Length > 0
                             && config.QueryPaths.Any(x => path.Value.ToLower().Contains(x.ToLower()))))
                         {
                             // Read the token out of the query string
                             context.Token = accessToken;
                         }
                         return Task.CompletedTask;
                     }
                 };
             }
         });*/

    }




    public static bool DecorateService<TService, TImpl>(this IServiceCollection services, params object[] parameters)
    {
        var existingService = services.SingleOrDefault(s => s.ServiceType == typeof(TService));
        if (existingService is null)
            throw new ArgumentException($"No service of type {typeof(TService).Name} found.");

        ServiceDescriptor? newService;

        if (existingService.ImplementationType is not null)
        {
            newService = new ServiceDescriptor(existingService.ServiceType,
                sp =>
                {
                    TService inner =
                        (TService)ActivatorUtilities.CreateInstance(sp, existingService.ImplementationType);

                    if (inner is null)
                        throw new Exception(
                            $"Unable to instantiate decorated type via implementation type {existingService.ImplementationType.Name}.");

                    var parameters2 = new object[parameters.Length + 1];
                    Array.Copy(parameters, 0, parameters2, 1, parameters.Length);
                    parameters2[0] = inner;

                    return ActivatorUtilities.CreateInstance<TImpl>(sp, parameters2)!;
                },
                existingService.Lifetime);
        }
        else if (existingService.ImplementationInstance is not null)
        {
            newService = new ServiceDescriptor(existingService.ServiceType,
                sp =>
                {
                    TService inner = (TService)existingService.ImplementationInstance;
                    return ActivatorUtilities.CreateInstance<TImpl>(sp, inner, parameters)!;
                },
                existingService.Lifetime);
        }
        else if (existingService.ImplementationFactory is not null)
        {
            newService = new ServiceDescriptor(existingService.ServiceType,
                sp =>
                {
                    TService inner = (TService)existingService.ImplementationFactory(sp);
                    if (inner is null)
                        throw new Exception(
                            "Unable to instantiate decorated type via implementation factory.");

                    return ActivatorUtilities.CreateInstance<TImpl>(sp, inner, parameters)!;
                },
                existingService.Lifetime);
        }
        else
        {
            throw new Exception(
                "Unable to instantiate decorated type.");
        }

        services.Remove(existingService);
        services.Add(newService);

        return true;
    }

    public static void UseTenatAuth(this WebApplication app)
    {
        app.UseAuthentication();
        app.UseAuthorization();
    }


    // 定义JWT身份验证选项类
    public class JwtAuthenticationOptions : AuthenticationSchemeOptions
    {
        public string SecretKey { get; set; }
        public bool ValidateIssuer { get; set; } = true;
        public string Issuer { get; set; }
        public bool ValidateAudience { get; set; } = true;
        public string Audience { get; set; }
    }


}
